﻿using System;
using System.Data;
using System.Xml;
using System.Collections.Generic;
using SdmxMl.Common;
using SdmxMl.Structure;
using SdmxMl.Manager;
using SdmxMl.Multiplexor;
using System.Text;

namespace SmsMeta.StructureRequest
{
    public class BaseArtefactRequest
    {
        protected QueryHelper queryHlp;

        public BaseArtefactRequest(QueryHelper qh)
        {
            queryHlp = qh;
        }

        public virtual XmlDocument GetSdmxMl()
        {
            XmlDocument xDoc = new XmlDocument();
            SmManager sm = new SmManager();

            ArtefactRefTypedList artl = GetMatching(queryHlp);
            if (artl.Count > 0)
            {
                // What kind of detail is required ?
                ArtefactDetail ard = queryHlp.QueryDetail != ArtefactDetail.AllStubs ? ArtefactDetail.Full : ArtefactDetail.AllStubs;
                sm.LoadMissingArtefactsFromRegistry(artl, ard);

                // now refered artefacts (if any)
                if (queryHlp.QueryDetail != ArtefactDetail.AllStubs && queryHlp.IsChildReferencesRequested)
                    PrepareReferences(sm);
            }

            xDoc = sm.GetAsDocument();

            return xDoc;
        }

        protected virtual void PrepareReferences(SmManager sm)
        {
            ArtefactDetail ard = queryHlp.QueryDetail == ArtefactDetail.Full ? ArtefactDetail.Full : ArtefactDetail.AllStubs;

            // For each artefact, fetch references & add
            ArtefactManager am = sm.GetArtefactManager(queryHlp.ArTyped.Type);


            //Now if proxy stub requested only, get name in list
            if (ard == ArtefactDetail.AllStubs)
            {
                foreach (Artefact a in am.ArtefactList)
                {
                    ArtefactRefTypedList artl = a.ArtefactRefList;
                    foreach (ArtefactRefTyped art in artl)
                    {
                        // if not yet recorded
                        if (sm.GetArtefact(art) == null)
                        {
                            // add name from list of existing artefact
                            UpdateStubName(art);
                            sm.LoadMissingArtefactsFromRegistry(new ArtefactRefTypedList(art), ard);
                        }
                    }
                }
            }
            else
            {
                foreach (Artefact a in am.ArtefactList)
                    sm.LoadMissingArtefactsFromRegistry(a.ArtefactRefList, ard);
            }
        }

        protected ArtefactRefTypedList GetMatching(QueryHelper queryHlp)
        {
            ArtefactRefTypedList artl = new ArtefactRefTypedList();
            DataSet ds = new DataSet();
            if (queryHlp.IsAllAgencies == false)
            {
                ds = Mux.Instance.DirInfo(queryHlp.ArTyped.Type.ToString(), queryHlp.ArTyped.AgencyId.ToUpper()+"+X+1.0");
            }
            else
            {
                ds = Mux.Instance.DirInfo(queryHlp.ArTyped.Type.ToString());
            }

            if (ds.Tables.Count > 0)
            {
                // Setup Filter and Sort Criteria
                StringBuilder sbExpr = new StringBuilder();
                if (queryHlp.IsAllId == false)
                    sbExpr.Append("ID like '").Append(queryHlp.ArTyped.Id).Append("'");
                if (queryHlp.IsAllAgencies == false)
                {
                    if (sbExpr.Length > 0)
                        sbExpr.Append(" AND ");
                    sbExpr.Append("AI like '").Append(queryHlp.ArTyped.AgencyId).Append("'");
                }
                if (queryHlp.IsUnspecifiedVersion == false)
                {
                    if (sbExpr.Length > 0)
                        sbExpr.Append(" AND ");
                    sbExpr.Append("VER like '").Append(queryHlp.ArTyped.Version).Append("'");
                }

                string strSort = "ID ASC";
                DataRow[] foundRows = ds.Tables[0].Select(sbExpr.ToString(), strSort);

                // Manages latest version only ?
                if (queryHlp.IsLatestVersion)
                {
                    Dictionary<string, VersionDataRow> dicoLatest = new Dictionary<string, VersionDataRow>();
                    foreach (DataRow dr in foundRows)
                    {
                        double ver = 0.0;
                        Double.TryParse(dr["VER"].ToString(), out ver);
                        string key = MakeKey(dr);
                        if (dicoLatest.ContainsKey(key))
                        {
                            if (dicoLatest[key].Version < ver)
                                dicoLatest[key] = new VersionDataRow(ver, dr);
                        }
                        else
                            dicoLatest[key] = new VersionDataRow(ver, dr);
                    }

                    // Recreate as array
                    foundRows = new DataRow[dicoLatest.Values.Count];
                    int i = 0;
                    foreach (VersionDataRow vdr in dicoLatest.Values)
                        foundRows[i++] = vdr.DataR;
                }

                // Now fetch
                foreach (DataRow dr in foundRows)
                {
                    ArtefactRefTyped art = new ArtefactRefTyped(queryHlp.ArTyped.Type, MakeFullId(dr));
                    art.Name = GetInternationalizedName(dr);
                    art.IsFinal = IsFinalized(dr);
                    artl.Add(art);
                }
            }

            return artl;
        }

        /// <summary>
        ///  from refered artefact, look in directory list in order to fetch Name information and update artefact ref info
        /// </summary>
        /// <param name="art"></param>
        protected void UpdateStubName(ArtefactRefTyped art)
        {

            DataRow[] foundRows = null;
            DataSet ds = Mux.Instance.DirInfo(art.Type.ToString(), art.AgencyId);
            if (ds.Tables.Count > 0)
            {
                foundRows = ds.Tables[0].Select(string.Format("ID like '{0}' AND AI like '{1}' AND VER like '{2}'",
                    art.Id, art.AgencyId, art.Version));
            }
            // Now fetch
            if (foundRows != null && foundRows.Length > 0)
                art.Name = GetInternationalizedName(foundRows[0]);
            else
                art.Name.UpdateDefaultText("not found");
        }

        private string MakeKey(DataRow dr)
        {
            return dr["AI"].ToString() + "+" + dr["ID"].ToString();
        }

        private bool IsFinalized(DataRow dr)
        {
            return Convert.ToBoolean(dr["IF"]);
        }

        private string MakeFullId(DataRow dr)
        {
            return dr["AI"].ToString() + "+" + dr["ID"].ToString() + "+" + dr["VER"];
        }

        private MultiLang GetInternationalizedName(DataRow dr)
        {
            MultiLang ml = new MultiLang();
            foreach (string lg in LangSupport.SupportedLanguages)
                ml.UpdateForLanguage(lg, dr["Name_" + lg].ToString());

            return ml;
        }
    }


    internal class VersionDataRow
    {
        public DataRow DataR { get; set; }
        public double Version { get; set; }
        public VersionDataRow(double v, DataRow dr)
        {
            Version = v;
            DataR = dr;
        }
    }

}
